﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using EnvDTE;
using Microsoft.VisualStudio;

namespace Expect.VisualStudio.Extensions
{    
    public static class SolutionHelpers
    {
        public static bool HasFolder(this ProjectItems items, string foldename)
        {
            foreach (ProjectItem item in items)
            {
                if (item.Kind == EnvDTE.Constants.vsProjectItemKindPhysicalFolder)
                {
                    if (item.Name.ToLower() == foldename.ToLower()) return true;
                }
            }

            return false;
        }

        public static void GetProjectItemsForClassNames(this EnvDTE80.DTE2 dte, Dictionary<string, List<ProjectItem>> lst, params string[] classnames)
        {
            dte.Solution.GetProjectItemForClassName(lst, classnames);
        }

        public static void GetProjectItemForClassName(this Solution solution, Dictionary<string, List<ProjectItem>> lst, params string[] classnames)
        {
            foreach (Project project in solution)
            {
                project.GetProjectItemForClassName(lst, classnames);
            }
        }

        public static ProjectItem GetProjectItemForClassName(this Project project, Dictionary<string, List<ProjectItem>> lst, params string[] classnames)
        {
            foreach (ProjectItem item in project.ProjectItems)
            {
                if (item.FileCodeModel != null)
                {
                    foreach (var classname in classnames)
                    {
                        ProcessItem(item, classname, lst);
                    }

                }
                item.ProjectItems.GetProjectItemForClassName(lst, classnames);
            }
            return null;
        }

        private static void ProcessItem(ProjectItem item, string classname, Dictionary<string, List<ProjectItem>> lst)
        {
            var eltclass = item.FileCodeModel.CodeElements.GetClass(classname);
            if (eltclass != null)
            {
                List<ProjectItem> lstclasses;
                if (!lst.ContainsKey(classname))
                {
                    lstclasses = new List<ProjectItem>();
                    lst.Add(classname, lstclasses);
                }
                else
                {
                    lstclasses = lst[classname];
                }
                lstclasses.Add(item);
            }
        }

        public static void GetProjectItemForClassName(this ProjectItems items, Dictionary<string, List<ProjectItem>> lst, params string[] classnames)
        {
            foreach (ProjectItem item in items)
            {
                string itemname = item.Name;
                if (item.FileCodeModel != null && !item.Name.EndsWith(".expect.cs"))
                {
                    foreach (var classname in classnames)
                    {
                        ProcessItem(item, classname, lst);
                    }
                }

                item.ProjectItems.GetProjectItemForClassName(lst, classnames);
            }
        }

        public static Project GetProjectForFileName(this EnvDTE80.DTE2 dte, string filename)
        {
            foreach (Project prj in dte.Solution.Projects)
            {
                if (prj.ProjectItems.HasFileName(filename)) return prj;
            }

            return null;
        }

        public static bool HasFileName(this ProjectItems items, string filename)
        {
            foreach (ProjectItem item in items)
            {
                string fname = item.FileNames[0].ToLower();
                if (fname == filename) return true;

                if (item.ProjectItems.HasFileName(filename)) return true;
            }

            return false;
        }

        public static List<ProjectItem> GetAllProjectItem(this EnvDTE.Project project)
        {
            List<ProjectItem> retval = new List<ProjectItem>();
            Queue<ProjectItem> items = new Queue<ProjectItem>();
            foreach (ProjectItem item in project.ProjectItems)
                items.Enqueue(item);
            while (items.Count != 0)
            {
                ProjectItem item = items.Dequeue();
                retval.Add(item);
                if (item.ProjectItems != null)
                    foreach (ProjectItem subitem in item.ProjectItems)
                        items.Enqueue(subitem);
            }
            return retval;
        }

        public static bool IsPhysicalFile(this ProjectItem projectItem)
        {
            return string.Equals(projectItem.Kind, VSConstants.GUID_ItemType_PhysicalFile.ToString("B"), StringComparison.InvariantCultureIgnoreCase);
        }

        public static string GetFileContent(this ProjectItem projectItem)
        {
            if (projectItem.get_IsOpen(EnvDTE.Constants.vsViewKindAny))
            {
                TextDocument textDoc = (TextDocument)projectItem.Document.Object("TextDocument");
                EditPoint start = textDoc.StartPoint.CreateEditPoint();
                return start.GetText(textDoc.EndPoint);
            }
            else
            {
                using (TextReader file = new StreamReader(GetFileName(projectItem)))
                {
                    return file.ReadToEnd();
                }
            }
        }

        public static string GetFileName(this ProjectItem projectItem)
        {
            return projectItem.get_FileNames(1);
        }

        public static string GetRelativePath(string path, string basePath)
        {
            path = Path.GetFullPath(path);
            basePath = Path.GetFullPath(basePath);
            if (string.Equals(path, basePath, StringComparison.OrdinalIgnoreCase))
                return "."; // the "this folder"

            if (path.StartsWith(basePath + Path.DirectorySeparatorChar, StringComparison.OrdinalIgnoreCase))
                return path.Substring(basePath.Length + 1);

            //handle different drives
            string pathRoot = Path.GetPathRoot(path);
            if (!string.Equals(pathRoot, Path.GetPathRoot(basePath), StringComparison.OrdinalIgnoreCase))
                return path;

            //handle ".." pathes
            string[] pathParts = path.Substring(pathRoot.Length).Split(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar);
            string[] basePathParts = basePath.Substring(pathRoot.Length).Split(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar);

            int commonFolderCount = 0;
            while (commonFolderCount < pathParts.Length && commonFolderCount < basePathParts.Length &&
                string.Equals(pathParts[commonFolderCount], basePathParts[commonFolderCount], StringComparison.OrdinalIgnoreCase))
                commonFolderCount++;

            StringBuilder result = new StringBuilder();
            for (int i = 0; i < basePathParts.Length - commonFolderCount; i++)
            {
                result.Append("..");
                result.Append(Path.DirectorySeparatorChar);
            }

            if (pathParts.Length - commonFolderCount == 0)
                return result.ToString().TrimEnd(Path.DirectorySeparatorChar);

            result.Append(string.Join(Path.DirectorySeparatorChar.ToString(), pathParts, commonFolderCount, pathParts.Length - commonFolderCount));
            return result.ToString();
        }

        public static IEnumerable<string> EmbeddedImages(this Project project)
        {
            foreach (var embeddedImage in project.ProjectItems.EmbeddedImages())
            {
                yield return embeddedImage;
            }
        }

        public static IEnumerable<string> EmbeddedImages(this ProjectItems items)
        {
            foreach (ProjectItem projectItem in items)
            {
                var img = IsImageItem(projectItem);
                if (!string.IsNullOrEmpty(img)) 
                    yield return img;
                else
                {
                    foreach (var item in EmbeddedImages(projectItem.ProjectItems))
                    {
                        yield return item;
                    }
                }
            }
        }

        private static string IsImageItem(ProjectItem item)
        {
            string path = item.FileNames[0];
            if (IsImage(path)) return path;

            return null;
        }

        private static bool IsImage(string filepath)
        {
            string ext = Path.GetExtension(filepath).ToLower();
            if (ext == ".jpg" || ext == ".jpeg" || ext == ".bmp" || ext == ".png" || ext == ".tiff")
            {
                return true;
            }

            return false;
        }
    }
}
